package com.huimark.jaxws;

import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.xml.ws.WebServiceException;

import com.sun.istack.NotNull;
import com.sun.xml.ws.api.server.Container;
import com.sun.xml.ws.resources.WsservletMessages;
import com.sun.xml.ws.transport.http.DeploymentDescriptorParser;
import com.sun.xml.ws.transport.http.servlet.JAXWSRIDeploymentProbeProvider;
import com.sun.xml.ws.transport.http.servlet.ServletAdapter;
import com.sun.xml.ws.transport.http.servlet.ServletAdapterList;
import com.sun.xml.ws.transport.http.servlet.ServletUtil;
import com.sun.xml.ws.transport.http.servlet.WSServlet;
import com.sun.xml.ws.transport.http.servlet.WSServletDelegate;


/**
 * 重新实现WSServletContextListener的逻辑，主要是只有在springMVC初始化以后SpringBeanAutowiringSupport才能生效
 * 也就是autowired才能生效，因为这个时候 ContextLoader.getCurrentWebApplicationContext()才有值
 * @author lenovo
 *
 */
public class WSSpringServlet2 extends WSServlet {
	private static final long serialVersionUID = -1108807654066546384L;
	static final String JAXWS_RI_RUNTIME = "/WEB-INF/sun-jaxws.xml";
	private WSServletDelegate delegate;
	private List<ServletAdapter> adapters;
	private final JAXWSRIDeploymentProbeProvider probe = new JAXWSRIDeploymentProbeProvider();
	private static final String WSSERVLET_CONTEXT_LISTENER_INVOKED = "com.sun.xml.ws.transport.http.servlet.WSServletContextListener.Invoked";

	private static final Logger logger = Logger
			.getLogger(com.sun.xml.ws.util.Constants.LoggingDomain
					+ ".WSSpringServlet2.http");

	@Override
	public void destroy() {
		super.destroy();
		if (delegate != null) { // the deployment might have failed.
			delegate.destroy();
		}
		if (adapters != null) {
			for (ServletAdapter a : adapters) {
				try {
					a.getEndpoint().dispose();
				} catch (Throwable e) {
					logger.log(Level.SEVERE, e.getMessage(), e);
				}
				// Emit undeployment probe event for each endpoint
				probe.undeploy(a);
			}
		}

		if (logger.isLoggable(Level.INFO)) {
			logger.info(WsservletMessages.LISTENER_INFO_DESTROY());
		}
	}

	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		super.init();
		contextInitialized(servletConfig.getServletContext());
	}

	public void contextInitialized(ServletContext context) {
		if (logger.isLoggable(Level.INFO)) {
			logger.info(WsservletMessages.LISTENER_INFO_INITIALIZE());
		}
		parseAdaptersAndCreateDelegate(context);
		if (adapters != null) {
			// Emit deployment probe event for each endpoint
			for (ServletAdapter adapter : adapters) {
				probe.deploy(adapter);
			}
		}
	}

	void parseAdaptersAndCreateDelegate(ServletContext context) {
		// The same class can be invoked via @WebListener discovery or explicit
		// configuration in deployment descriptor
		// avoid redoing the processing of web services.
		// String alreadyInvoked = (String) context
		// .getAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED);
		// if (Boolean.valueOf(alreadyInvoked)) {
		// return;
		// }
		context.setAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED, "true");
		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();
		if (classLoader == null) {
			classLoader = getClass().getClassLoader();
		}
		try {
			URL sunJaxWsXml = context.getResource(JAXWS_RI_RUNTIME);
			if (sunJaxWsXml == null) {
				throw new WebServiceException(
						WsservletMessages.NO_SUNJAXWS_XML(JAXWS_RI_RUNTIME));
			}

			// Parse the descriptor file and build endpoint infos
			DeploymentDescriptorParser<ServletAdapter> parser = new DeploymentDescriptorParser<ServletAdapter>(
					classLoader, new ServletResourceLoader(context),
					createContainer(context), new ServletAdapterList(context));
			adapters = parser.parse(sunJaxWsXml.toExternalForm(),
					sunJaxWsXml.openStream());
			registerWSServlet(adapters, context);
			delegate = createDelegate(adapters, context);

			context.setAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO, delegate);

		} catch (Throwable e) {
			logger.log(Level.SEVERE,
					WsservletMessages.LISTENER_PARSING_FAILED(e), e);
			context.removeAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO);
			throw new WSServletException("listener.parsingFailed", e);
		}
	}

	private void registerWSServlet(List<ServletAdapter> adapters,
			ServletContext context) {
		if (!ServletUtil.isServlet30Based())
			return;
		Set<String> unregisteredUrlPatterns = new HashSet<String>();
		try {
			Collection<? extends ServletRegistration> registrations = context
					.getServletRegistrations().values();
			for (ServletAdapter adapter : adapters) {
				if (!existsServletForUrlPattern(adapter.urlPattern,
						registrations)) {
					unregisteredUrlPatterns.add(adapter.urlPattern);
				}
			}
			if (!unregisteredUrlPatterns.isEmpty()) {
				// register WSServlet Dynamically
				ServletRegistration.Dynamic registration = context.addServlet(
						"Dynamic JAXWS Servlet", WSServlet.class);
				registration.addMapping(unregisteredUrlPatterns
						.toArray(new String[unregisteredUrlPatterns.size()]));
				registration.setAsyncSupported(true);

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean existsServletForUrlPattern(String urlpattern,
			Collection<? extends ServletRegistration> registrations) {
		for (ServletRegistration r : registrations) {
			if (r.getMappings().contains(urlpattern)) {
				return true;
			}
		}
		return false;
	}

	protected @NotNull Container createContainer(ServletContext context) {
		return new ServletContainer(context);
	}

	protected @NotNull WSServletDelegate createDelegate(
			List<ServletAdapter> adapters, ServletContext context) {
		return new WSServletDelegate(adapters, context);
	}
}
